#include "DataBase.h"

DataBase::DataBase(Label* labelInfo)
{
	info = labelInfo;
	//string dbpath = "music.db";
	sqlite3_open(dbpath.c_str(), &pdb);
	//thread buildDB([&]
	//{
	createMusicTables();
	scanFiles(string("musicdata"));
	writeMusicDB();
//});
//buildDB.join();
}

DataBase::~DataBase()
{
	sqlite3_close(pdb);
}

void DataBase::scanFiles(string path)
{
	if (info != nullptr) info->setString("Scaning beatmap...");
	_finddata_t find;
	string filename = string(path).append("/*");
	auto hfile = _findfirst(filename.c_str(), &find);
	if (hfile == -1)
	{
		MessageBox("No Beatmap found.", "Infomation");
	}
	else
	{
		do
		{
			if (find.attrib&_A_SUBDIR)
			{
				if (strcmp(find.name, ".") && strcmp(find.name, ".."))
				{
					scanFiles(string(path).append("/").append(find.name));
				}
				else continue;
			}
			else
			{
				auto splitname = strtool::split(string(find.name), ".");
				if (splitname.size() > 0)
				{
					auto fileext = splitname[splitname.size() - 1];
					if (fileext.compare("osu") == 0)
						osuFiles.push_back(string(path).append("/").append(find.name));
					else if (fileext.compare("bms") == 0)
						bmsFiles.push_back(string(path).append("/").append(find.name));
					else if (fileext.compare("bme") == 0)
						bmsFiles.push_back(string(path).append("/").append(find.name));
				}
			}
		} while (_findnext(hfile, &find) == 0);
	}
}

void DataBase::writeMusicDB()
{
	//for (vector<string>::iterator iter = osuFiles.begin(); iter != osuFiles.end(); iter++)
	//{
	//	CCLOG("%s", iter->c_str());
	//}
	int filecount = 0;
	auto fileAnalyze = [&](string filepath, NoteManager::FILETYPE filetype)
	{
		if (info != nullptr)
		{
			string infostr = filepath.c_str();
			#if !defined(COCOS2D_DEBUG) || COCOS2D_DEBUG == 0
			infostr = "Scanning[" + strtool::int2str(filecount) + "]: " + infostr;
			#else
			infostr = "Scanning files ... [" + strtool::int2str(filecount) + "]";
			#endif
			info->setString(infostr);
		}
		NoteManager *pnm = NoteManager::create((filepath.c_str()), filetype);
		if (pnm == nullptr)
			return;
		char *selset = sqlite3_mprintf("select * from music_set where setid=%d", pnm->setId);
		char **selectdata;
		int row, col;
		sqlite3_get_table(pdb, selset, &selectdata, &row, &col, NULL);
		sqlite3_free(selset);
		sqlite3_free_table(selectdata);
		if (row <= 0)
		{
			time_t tm;
			tm = time(NULL);
			auto l_tm = localtime(&tm);
			char *regdate = new char[9];
			strftime(regdate, 9, "%Y%m%d", l_tm);
			//sprintf_s(time, 9, "%s", ctime(&tm));
			char *insset = sqlite3_mprintf("insert into music_set values(%d,%d,'%q','%q','%q','%q','%q',%d,'%q','%q')",
										   MODE_CLASSIC,
										   pnm->setId,
										   pnm->metaData.titleU.size() > 0 ? pnm->metaData.titleU.c_str() : pnm->metaData.title.c_str(),
										   pnm->metaData.artistU.size() > 0 ? pnm->metaData.artistU.c_str() : pnm->metaData.artist.c_str(),
										   pnm->metaData.source.c_str(),
										   pnm->metaData.tag.c_str(),
										   pnm->musicFile.c_str(),
										   pnm->previewPoint,
										   regdate,
										   pnm->dataPath.c_str()
			);
			sqlite3_exec(pdb, insset, NULL, NULL, NULL);
			free(regdate);
			sqlite3_free(insset);
		}
		char *insnote = sqlite3_mprintf("insert into music_note values(%d,%d,%d,%d,%f,%f,%Q,%Q,%Q,%d,NULL,NULL,NULL,NULL,NULL,NULL,%Q,%Q)",
										pnm->mapId,
										pnm->setId,
										filetype,
										pnm->trackCount,
										pnm->bpmMax,
										pnm->bpmMin,
										pnm->bgFile.c_str(),
										pnm->metaData.noter.c_str(),
										pnm->metaData.version.c_str(),
										pnm->level,
										pnm->dataPath.c_str(),
										filepath.c_str()
		);
		sqlite3_exec(pdb, insnote, NULL, NULL, NULL);
		sqlite3_free(insnote);
		filecount++;
	};
	//for (auto iter : bmsFiles)
	//	fileAnalyze(iter, NoteManager::FILETYPE::FILE_BMS);
	//for (auto iter : osuFiles)
	//	fileAnalyze(iter, NoteManager::FILETYPE::FILE_OSU);
	int fileType = 2;
	size_t fileCount = 0;
	size_t fileVecSize = 0;
	int threadId = 0;
	struct threads
	{
		thread t;
		bool s = false;
	};
	threads* fileThreads = new threads[10];
	while (fileType <= 2)
	{
		if (fileType == 1) fileVecSize = osuFiles.size();
		else fileVecSize = bmsFiles.size();
		while (fileCount < fileVecSize)
		{
			threadId = threadId % 10;
			if (!fileThreads[threadId].s)
			{
				auto fn = [=, &fileThreads]()mutable
				{
					if (fileType == 1) fileAnalyze(osuFiles[fileCount], NoteManager::FILETYPE::FILE_OSU);
					else fileAnalyze(bmsFiles[fileCount], NoteManager::FILETYPE::FILE_BMS);
					fileThreads[threadId].s = false;
				};
				fileThreads[threadId].s = true;
				fileThreads[threadId].t = thread(fn);
				fileCount++;
				fileThreads[threadId].t.detach();
			}
			threadId++;
			//Sleep(1);
		}
		for (int i = 0; i < 10; i++) if (fileThreads[i].s) fileThreads[i].t.join();
		fileType++;
		fileCount = 0;
	}
	sqlite3_close(pdb);
	if (info != nullptr) info->setString(strtool::int2str(filecount) + " files scanned.");
}

void DataBase::createMusicTables()
{
	string sql = "DROP TABLE [MUSIC_SET];DROP TABLE [MUSIC_NOTE];";
	sqlite3_exec(pdb, sql.c_str(), NULL, NULL, NULL);
	sql = getMusicDDL();
	sqlite3_exec(pdb, sql.c_str(), NULL, NULL, NULL);
	if (info != nullptr) info->setString("Database initializing...");
}

NoteManager * DataBase::getRandomMusicNote()
{
	sqlite3_open(dbpath.c_str(), &pdb);
	char *sql = "select notefile,filetype from music_note order by random() limit 1";
	char **selectdata;
	sqlite3_get_table(pdb, sql, &selectdata, NULL, NULL, NULL);
	sqlite3_close(pdb);
	auto ret = NoteManager::create(selectdata[2], NoteManager::FILETYPE(atoi(selectdata[3])));
	//free(sql);
	sqlite3_free(selectdata);
	return ret;
}

NoteManager * DataBase::getMusicNoteBySetId(string setid)
{
	sqlite3_open(dbpath.c_str(), &pdb);
	char *sql = sqlite3_mprintf("select notefile,filetype from music_note where setid=%Q order by KEYS,LEVEL", setid.c_str());
	char **selectdata;
	sqlite3_get_table(pdb, sql, &selectdata, NULL, NULL, NULL);
	sqlite3_close(pdb);
	auto ret = NoteManager::create(selectdata[2], NoteManager::FILETYPE(atoi(selectdata[3])));
	sqlite3_free(sql);
	sqlite3_free(selectdata);
	return ret;
}

NoteManager * DataBase::getMusicNoteBySetAndNoteId(string setid, string noteid)
{
	sqlite3_open(dbpath.c_str(), &pdb);
	char *sql = sqlite3_mprintf("select notefile,filetype from music_note where setid=%Q and id=%Q", setid.c_str(), noteid.c_str());
	char **selectdata;
	sqlite3_get_table(pdb, sql, &selectdata, NULL, NULL, NULL);
	sqlite3_close(pdb);
	auto ret = NoteManager::create(selectdata[2], NoteManager::FILETYPE(atoi(selectdata[3])));
	sqlite3_free(sql);
	sqlite3_free(selectdata);
	return ret;
}

string DataBase::getMusicDDL()
{
	return "CREATE TABLE [MUSIC_SET] (\r\n\
				[MODE] INT(4),\r\n\
				[SETID] INT(32),\r\n\
				[TITLE] NVARCHAR,\r\n\
				[ARTIST] NVARCHAR,\r\n\
				[SOURCE] NVARCHAR,\r\n\
				[TAGS] NVARCHAR,\r\n\
				[MUSICFILE] NVARCHAR,\r\n\
				[MUSICPREVIEWPOINT] INT(32),\r\n\
				[REGISTERED] INT,\r\n\
				[DATAPATH] NVARCHAR);\r\n\
			CREATE UNIQUE INDEX[IDX_SET] ON[MUSIC_SET]([SETID]);\r\n\
			\r\n\
			CREATE TABLE[MUSIC_NOTE](\r\n\
				[ID] INT(32),\r\n\
				[SETID] INT(32),\r\n\
				[FILETYPE] INT(4),\r\n\
				[KEYS] INT(8),\r\n\
				[BPMMAX] FLOAT,\r\n\
				[BPMMIN] FLOAT,\r\n\
				[BACKGROUNDFILE] NVARCHAR,\r\n\
				[NOTER] NVARCHAR,\r\n\
				[DIFFICULTY] NVARCHAR,\r\n\
				[LEVEL] INT,\r\n\
				[LASTPLAYED] INT,\r\n\
				[HIGHSCORE] INT(32),\r\n\
				[HIGHEVALUATION] INT(8),\r\n\
				[HIGHACCURACY] FLOAT,\r\n\
				[ISFULLCOMBO] BOOL,\r\n\
				[ISCLEARED] BOOL,\r\n\
				[DATAPATH] NVARCHAR,\r\n\
				[NOTEFILE] NVARCHAR);\r\n\
			CREATE UNIQUE INDEX [SET_NOTE_ID] ON [MUSIC_NOTE] ([ID], [SETID]);\r\n\
			\r\n\
			CREATE TABLE [MUSIC_TAG] (\r\n\
			[TAG] NVARCHAR,\r\n\
			[SETIDCOL] CLOB);\r\n\
			CREATE INDEX[IDX_TAG] ON[MUSIC_TAG]([TAG]); \r\n";
}
